from datetime import datetime
from flask import session
from . import passport_bp
from flask import request,abort,make_response,jsonify,current_app
from info.utlis.captcha.captcha import captcha
from info import redis_store,constants, db
import json,re
from info.utlis.response_code import RET
from info.models import User
import random
from info.lib.yuntongxun.sms import CCP

# 用户注册接口
@passport_bp.route('/register',methods=['post'])
def register():
    """
       1.获取参数
           1.1 手机号码 用户填写的短信验证码 密码（未加密）
       2.校验参数
           2.1 非空判断
           2.2 手机号码格式判断
       3.逻辑处理
           3.1 根据手机号码拼接SMS_18520340804这个key去redis获取真实短信验证码
           3.2 对比用户添加的短信验证码和真实的短信验证码对比
           3.3 一致：根据User模型创建用户对象，保存到数据库
           3.4 一般：用户注册成功 第一次都会给用户自动登录，使用session存储用户信息
       4.返回值处理
       """
#     1.获取参数
           #1.1 手机号码 用户填写的短信验证码 密码（未加密）
#     2.校验参数
        #2.1 非空判断
        #2.2 手机号码格式判断
    parms_dict = request.json
    mobile = parms_dict.get("mobile")
    smscode = parms_dict.get("smscode")
    password = parms_dict.get("password")
    # 2.校验参数
    # 2.1 手机号码 用户填写的图片验证码值 UUID随机值编号判空校验
    if not all([mobile, smscode, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")
        # 2.2 手机号码格式是否正确
    if not re.match('^1[35789][0-9]{9}', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")
    #3.逻辑处理
        #3.1 根据手机号码拼接SMS_18520340804这个key去redis获取真实短信验证码
    try:
        real_sms_code = redis_store.get('smscode_%s' % mobile)
        if real_sms_code:
            redis_store.delete('smscode_%s' % mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询短信码异常")
        #3.2 对比用户添加的短信验证码和真实的短信验证码对比
    if smscode != real_sms_code:
        return jsonify(errno=RET.PARAMERR,errmsg="短信验证码输入错误")
        #3.3 一致：根据User模型创建用户对象，保存到数据库
    #user = User(mobile=mobile,nick_name = mobile,password = password)
    #r2 = Role(name="user")
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    # TODO: 需要将密码加密后赋值给password_hash
    # user.make_password_hash(password),使用property方法
    user.password = password
    # 记录最后一次登录时间
    user.last_login = datetime.now()
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR,errmsg="保存用户数据失败")

    #3.4 一般：用户注册成功 第一次都会给用户自动登录，使用session存储用户信息
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name
    session["is_admin"] = False

    return jsonify(errno=RET.OK,errmsg="用户注册成功")

# 用户登录接口
@passport_bp.route('/login',methods=['post'])
def login():
    """
        1.获取参数
            1.1 手机号码 密码（未加密的）
        2.校验参数
            2.1 非空判断
            2.2 手机号码格式
        3.逻辑处理
            3.1 根据手机号码查询用户对象
            3.2 对比用户填写的密码和用户对象中密码是否一致
            3.3 一致：记录最后一次登录时间 使用session记录用户登录信息
        4.返回值处理
    """
    parms_dict = request.json
    mobile = parms_dict.get("mobile")
    password = parms_dict.get("password")
    # 2.校验参数
    # 2.1 手机号码 用户填写的图片验证码值 UUID随机值编号判空校验
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")
        # 2.2 手机号码格式是否正确
    if not re.match('^1[35789][0-9]{9}', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")
    #3.逻辑处理
        #3.1 根据手机号码查询用户对象
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询用户手机号异常")
    if not user:
        return jsonify(errno=RET.PARAMERR,errmsg="该用户不存在")
        #3.2 对比用户填写的密码和用户对象中密码是否一致
    if not user.check_passowrd(password):
        return jsonify(errno=RET.DATAERR,errmsg="密码填写错误")
        #3.3 一致：记录最后一次登录时间 使用session记录用户登录信息
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name
    session["is_admin"] = False

    # 修改最后一次登录时间，需要提交
    user.last_login = datetime.now()
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR,errmsg="提交数据异常")

    return jsonify(errno=RET.OK,errmsg="登录成功")

# 用户退出接口
@passport_bp.route('/login_out',methods=['post'])
def login_out():
    session.pop("user_id")
    session.pop("mobile")
    session.pop("nick_name")
    session.pop("is_admin",None)
    return jsonify(errno=RET.OK,errmsg="用户退出成功")


# 发送图片验证码的图片
@passport_bp.route("/image_code")
def get_imagecode():
    # 获取验证码图片
    # 1.获取参数
    #1.1获取前端传上来
    imageCodeID = request.args.get("imageCodeID")

    #2.校验参数
    if not imageCodeID:
        abort(404)

    # captcha.generate_captcha()实例对象.方法
    name, text ,image = captcha.generate_captcha()
    try:
        redis_store.set("imagecode_%s"%imageCodeID,text,ex=constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        abort(500)
    response = make_response(image)
    response.headers["Content-Type"] = "image/html"
    return response


# 发送短信验证码
@passport_bp.route("/sms_code",methods=['post'])
def sms_code():
    """点击发送短信验证码接口"""
    """
    1.获取参数
        1.1  手机号码 用户填写的验证码值 UUID随机值编号
    2.校验参数
        2.1 手机号码 用户填写的验证码值 UUID随机值编号判空校验
        2.2 手机号码格式是否正确
    3.逻辑处理
        3.1 根据image_code_id编号去redis中获取验证码的真实值
        3.1.1 real_image_code 验证码的真实值有值： 删除Redis中的存储的值 防止多次利用real_image_code来验证
        3.1.2 没有值： 代表redis中验证码值过期了 --> 细节： 调用前端的再次生成一张图片

        3.2 比较用户填写的image_code和后端真实的real_image_code进行对比
        成功：发送短信验证码
        失败：验证码填写错误

        3.3.0 根据手机号码去查询手机号码是否注册过

        3.3 调用云通讯的SDK发送短信验证码
        3.3.1 生成一个6位的随机短信内容

        3.4 发送短信验证码成功
        3.4.1 存储短信验证码到redis

    4.返回值处理 告知前端发送短信验证成功请注意查收

    """
    # 1.获取参数
    # 1.手机号码 用户填写的验证码值 UUID随机值编号
    # 接收的是json字符串，转为json对象格式
    # param_dict = json.loads(request.data)
    parms_dict = request.json
    mobile = parms_dict.get("mobile")
    image_code = parms_dict.get("image_code")
    image_code_id = parms_dict.get("image_code_id")
    # 2.校验参数
        # 2.1 手机号码 用户填写的图片验证码值 UUID随机值编号判空校验
    if not all([mobile,image_code,image_code_id]):
        return jsonify(errno=RET.PARAMERR,errmsg="参数不足")
        # 2.2 手机号码格式是否正确
    if not re.match('1[35789][0-9]{9}',mobile):
        return jsonify(errno=RET.PARAMERR,errmsg="手机号格式错误")
    #3.逻辑处理
    try:
        # 3.1 根据image_code_id编号去redis中获取图片验证码的真实值
        real_image_code = redis_store.get("imagecode_%s"%image_code_id)

        #  3.1.1 real_image_code 验证码的真实值有值： 删除Redis
        # 中的存储的值 防止多次利用real_image_code来验证
        if real_image_code:
            redis_store.delete("imagecode_%s"%image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg="查询图片验证码真实值错误")
        #  3.1.2 没有值： 代表redis中验证码值过期了 --> 细节：调用前端的再次生成一张图片
    if not real_image_code:
        return jsonify(errno=RET.NODATA,errmsg="验证码过期了")

        # 3.2 比较用户填写的image_code和后端真实的real_image_code进行对比
    if image_code.lower() != real_image_code.lower():
        # 失败：验证码填写错误
        return jsonify(errno=RET.DATAERR,errmsg="验证码填写错误")
        #3.3.0 根据手机号码去查询手机号码是否注册过
    try:
        m1 = User.query.filter(User.mobile==mobile).first()
        if m1:
            return jsonify(errno=RET.DATAEXIST,errmsg="该手机号已经注册过")
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DEBUG,errmsg="查询手机号错误")

        # 3.3 调用云通讯的SDK发送短信验证码
        # 3.3.1 生成一个6位的随机短信内容
    sms_code = random.randint(0,999999)
    sms_code = '%06d'%sms_code
    #CCP()是实例对象
    result = CCP().send_template_sms(mobile, [sms_code, constants.IMAGE_CODE_REDIS_EXPIRES/60], 1)
    if result != 0:
        return jsonify(errno=RET.THIRDERR,errmsg="发送短信失败")

        # 3.4 发送短信验证码成功
        # 3.4.1 存储短信验证码到redis
    try:
        redis_store.set("smscode_%s"%mobile,sms_code,ex=constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR,errmsg="保存短信验证码到数据库异常")

    # 4.返回值处理 告知前端发送短信验证成功请注意查收
    return jsonify(errno=RET.OK,errmsg="发送短信验证码成功")